/*
 * Amadeus Confidential Information:
 * Unauthorized use and disclosure strictly forbidden.
 * @1998-2007 - Amadeus s.a.s - All Rights Reserved.
 */
package com.amadeus.spin.devtool.soap.shooter.filter;

import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

import com.amadeus.spin.devtool.soap.shooter.config.SoapShooterInitializationException;

public class FilterHandlerImpl implements FilterHandler {
  private static Logger log = Logger.getLogger(FilterHandlerImpl.class);

  private static final Pattern REPLACE_FILTER_PATTERN = Pattern.compile("name=(.*)\\|type=(.*)\\|value(\\+?=)(.*)");

  private Map<String, Filter> filters;

  @Override
  public void replaceFilters(String[] originalExpression) throws SoapShooterInitializationException {
    for (String expression : originalExpression) {
      Matcher m = REPLACE_FILTER_PATTERN.matcher(expression);
      if (m.matches() && m.groupCount() == 4) {
        String namePattern = m.group(1);
        String typePattern = m.group(2);
        String operation = m.group(3);
        String newValue = m.group(4);

        for (Entry<String, Filter> entry : filters.entrySet()) {
          if (filterMatches(namePattern, typePattern, entry)) {
            // Default operation is replacing the default value
            String valueToReplace = newValue;
            if (operation.equals("+=")) {
              valueToReplace = entry.getValue().getOriginalValue() + newValue;
            }
            addFilter(entry.getKey(), typePattern, valueToReplace);
            log.info(String.format("Filter replaced. Name : %s, Value : %s", entry.getKey(), valueToReplace));
          }
        }
      }
      else {
        log.error("Replace filter expression is not valid : " + expression);
        throw new SoapShooterInitializationException("Replace filter expression is not valid : " + expression);
      }
    }
  }

  /**
   * Checks if the filter implementation is elligible to the filter definition
   *
   * @param namePattern
   * @param type
   * @param entry
   * @return
   */
  private boolean filterMatches(String namePattern, String type, Entry<String, Filter> entry) {
    return Pattern.matches(namePattern, entry.getKey()) && entry.getValue().getTypeString().equalsIgnoreCase(type);
  }

  public FilterHandlerImpl() {
    filters = new TreeMap<String, Filter>();
  }

  @Override
  public void addFilter(String filterName, String filterType, String filterValue)
      throws SoapShooterInitializationException {
    Filter filter;

    // Filters can contain other filters in their values
    if (FilterImpl.STRING_TYPE.equalsIgnoreCase(filterType)) {
      filter = new StringFilter(filterName, filterValue);
    }
    else if (FilterImpl.DATE_TYPE.equalsIgnoreCase(filterType)) {
      filter = new DateFilter(filterName, filterValue);
    }
    else if (FilterImpl.INT_TYPE.equalsIgnoreCase(filterType)) {
      filter = new IntegerFilter(filterName, filterValue);
    }
    else {
      filter = new FilterImpl(filterName, filterValue);
      // log warning that using default filter type
    }

    filters.put(filter.getFilterName(), filter);
  }

  /**
   * Takes a string as input containing fields :- strings starting with ^^ and
   * finishing with ^. If a match is made with a filter name, the filter value
   * is applied to the string replacing the field.
   *
   * @param xmlIn
   *          target String to be parsed on which filters are to be applied
   * @param log
   *
   * @return String with filters applied
   */
  @Override
  public String applyFilters(String xmlIn) {
    int fieldStartIndex = 0;
    int k = 0;
    int j = 0;
    int l = 0;
    int startDefaultIdx = 0;
    int endDefaultIdx = 0;
    String field = null;
    String fieldName = null;
    String value = null;
    String fieldFormat = null;
    String defaultValue = null;
    Filter filter = null;

    do {
      fieldStartIndex = xmlIn.indexOf("^^");
      j = xmlIn.indexOf("^", fieldStartIndex + 2);
      if ((fieldStartIndex == -1) || (j == -1)) {
        // no (more) fields,
        // stop.
        break;
      }
      field = xmlIn.substring(fieldStartIndex + 2, j);
      k = field.indexOf("(");
      l = field.indexOf(")");
      if ((k == -1) || (l == -1)) {
        fieldName = field;
        // fieldType exist only for dates, indicates what format should be used. Standard is ETV.
      }
      else {
        fieldName = field.substring(0, k);
        fieldFormat = field.substring(k + 1, l);
      }

      startDefaultIdx = field.indexOf("[");
      endDefaultIdx = field.indexOf("]");
      if ((startDefaultIdx > -1) && (endDefaultIdx > -1)) {
        fieldName = field.substring(0, startDefaultIdx);
        defaultValue = field.substring(startDefaultIdx + 1, endDefaultIdx);
      }

      filter = getFilter(fieldName);
      if (filter != null) {
        if (fieldFormat != null) {
          value = filter.getFormattedValue(fieldFormat);
        }
        else {
          value = filter.getDefaultValue();
        }
      }
      else if (defaultValue != null) {
        // If no filter found but default value is present, apply it
        log.debug("filter name [" + fieldName + "] not found in list of valid filters so applying default value [" +
            defaultValue + "].");
        value = defaultValue;
      }
      else {
        // CSC MODIFIED: not always an error... could be within comments
        // log.error(this,"filter name ["
        // +fieldName+"] not found in list of valid filters of input script ["
        // + fileName + "].");
        log.warn("filter name [" + fieldName +
            "] not found in list of valid filters of input file. (maybe just in a commented block).");
        value = "";
      }
      xmlIn = xmlIn.substring(0, fieldStartIndex) + value + xmlIn.substring(j + 1);
    }
    while (fieldStartIndex != 1);
    return xmlIn;
  }

  private Filter getFilter(String filterName) {
    return filters.get(filterName);
  }

}
